#ifndef __SCENEBASE_H__
#define __SCENEBASE_H__

#pragma once

class COgreEngine;

enum ENetConfig
{
	e_NCClientR,
	e_NCClientL,
	e_NCServer,
	e_NCNone,
	e_NCMax
};

struct SEntity
{
	SEntity(String const &Name, String const &Filename, bool const &CastShadow = false) : m_Name(Name), m_Filename(Filename), m_CastShadow(CastShadow) {};

	String m_Name;
	String m_Filename;
	bool m_CastShadow;
};

struct SNode
{
	SNode(String const &Name, String const &EntityName, Vector3 const &Position = Vector3::ZERO, Quaternion const &Rotation = Quaternion::IDENTITY) : 
		m_Name(Name), m_EntityName(EntityName), m_Position(Position), m_Rotation(Rotation) {};

	String m_Name;
	String m_EntityName;
	Vector3 m_Position;
	Quaternion m_Rotation;
};

struct SLight
{
	SLight(String const &Name, Vector3 const &Position = Vector3::ZERO, ColourValue const &Diffuse = ColourValue::Black, ColourValue const &Specular = ColourValue::Black, Light::LightTypes const &Type = Light::LT_POINT, Vector3 const &Direction = Vector3(0.0f, -1.0f, 0.0f), bool const castShadow = true) : 
		m_Name(Name), m_Position(Position), m_Diffuse(Diffuse), m_Specular(Specular), m_Type(Type), m_Direction(Direction), m_CastShadow(castShadow) {};

	String m_Name;
	Vector3 m_Position;
	ColourValue m_Diffuse;
	ColourValue m_Specular;
	Light::LightTypes m_Type;
	Vector3 m_Direction;
	bool m_CastShadow;
};

struct SEvent
{
	SEvent(unsigned long const eventId, SOpenMoveDisplayPacket const &packet, Real startTime);
	SEvent(SEvent const &r) : m_Packet(r.m_Packet), m_pTimer(NULL), m_StartTime(r.m_StartTime), m_HasStarted(r.m_HasStarted), m_IsFirstTime(r.m_IsFirstTime), m_EventId(r.m_EventId)
	{
		m_pTimer = new OMTimer();
		m_pTimer->reset();
	}

	SEvent & operator = (SEvent const &r)
	{
		m_EventId = r.m_EventId;
		m_Packet = r.m_Packet;
		m_StartTime = r.m_StartTime;
		m_HasStarted = r.m_HasStarted;
		m_IsFirstTime = r.m_IsFirstTime;
		m_pTimer = new OMTimer();
		m_pTimer->reset();
		m_pTimer->setTime(r.m_pTimer->getMilliseconds());

		return (*this);
	}

	~SEvent(void);
	bool isOver(void);

	OMTimer * m_pTimer;
	SOpenMoveDisplayPacket m_Packet;
	Real m_StartTime;
	bool m_IsFirstTime;
	bool m_HasStarted;
	unsigned long m_EventId;
};

struct SProjectorFrustum
{
	SProjectorFrustum(Frustum * Decal, Frustum * Filter) : m_Decal(Decal), m_Filter(Filter) {}
	Frustum * m_Decal;
	Frustum * m_Filter;
};

class SceneLight : public Light
{
public:
	String m_Name;
};

struct SBoneTracker
{
	void *m_pObject;
	Bone *m_pBone;

	template<class Light>
	void SetPosition(Vector3 const &position)
	{
		static_cast<Light *>(m_pObject)->setPosition(position);
	}
};

struct STransition
{
	STransition(Real black, Real deform, Real add, bool deformAnim, Real duration, GpuProgramParametersSharedPtr fpParams, bool isActive, bool isBW) : 	m_black(black), m_deform(deform), m_add(add), m_deformAnim(deformAnim), m_duration(duration), m_fpParams(fpParams), m_isActive(isActive), m_isBW(isBW), m_realDuration(duration) {}
	Real m_black;
	Real m_deform;
	Real m_add;
	bool m_deformAnim;
	Real m_duration;
	GpuProgramParametersSharedPtr m_fpParams;
	bool m_isActive;
	bool m_isBW;
	Real m_realDuration;
};

class ExampleFrameListener;
class LightEditor;
class CEGUI::Scrollbar;
class XERCES_CPP_NAMESPACE::DOMElement;

class CSceneTimer
{
public:
	CSceneTimer(ENetConfig netConfig);

	Real getMilliseconds(void);
	Real getSeconds(void);
	void reset(void);
	void setTime(Real const val);

	bool m_UseManualTime;
	Real m_ManualTime;
	Real m_ManualSpeed;

	OMTimer m_timer;

	ENetConfig m_netConfig;
};

struct SCompositor
{
	SCompositor(char const * const name) : m_name(name), m_compositor(NULL) {};
	String m_name;
	CompositorInstance * m_compositor;
};

class CSceneBase : public FrameListener, public KeyListener, public CompositorInstance::Listener, public MouseMotionListener, public MouseListener
{
public:
	typedef std::vector<Ogre::FrameListener *> TFrameListeners;
	typedef std::vector<Ogre::KeyListener *> TKeyListeners;
	typedef std::vector<Ogre::Entity *> TEntities;
	typedef std::vector<Ogre::Light *> TLights;
	typedef std::vector<SceneLight> TSceneLights;
	typedef std::vector<Ogre::Frustum *> TFrustums;
	typedef std::vector<SEntity> TCreatorEntities;
	typedef std::vector<SNode> TCreatorNodes;
	typedef std::vector<SLight> TCreatorLights;
	typedef std::vector<SEvent> TEvents;
	typedef std::vector<SCompositor> TCompositors;

	enum EAnimationSpeed
	{
		e_SpeedPause = 0,
		e_SpeedHalf,
		e_SpeedNormal,
		e_SpeedX2,
		e_SpeedX4,
		e_SpeedMax
	};

	enum ENbCamera
	{
		e_NbCamera = 3,
	};

	struct SResetParams
	{
		SResetParams(Vector3 camPosition = Vector3::ZERO, Vector3 camDirection = -Vector3::UNIT_Z, ColourValue bgColor = ColourValue::Black, String skyboxName = "", int shadowTexSize = 512, Real shadowFarDist = 1000, ColourValue shadowColor = ColourValue(0.5, 0.5, 0.5, 0.5), Real aspectRatio = 1.33, Quaternion camOrientation = Quaternion::IDENTITY, Real duration = 120.0f) : m_camPosition(camPosition), m_camDirection(camDirection), m_bgColor(bgColor), m_skyboxName(skyboxName), m_shadowTexSize(shadowTexSize), m_shadowFarDist(shadowFarDist), m_shadowColor(shadowColor), m_aspectRatio(aspectRatio), m_camOrientation(camOrientation), m_duration(duration), m_realDuration(duration) {}

		Vector3 m_camPosition;
		Vector3 m_camDirection;
		ColourValue m_bgColor;
		String m_skyboxName;
		int m_shadowTexSize;
		Real m_shadowFarDist;
		ColourValue m_shadowColor;
		Real m_aspectRatio;
		Degree m_FOVy;
		Degree m_camSideAngle;
		Quaternion m_camOrientation;
		Real m_duration;
		Real m_realDuration;
	};

//Methods
public:
	CSceneBase(COgreEngine &Creator, const String & Name, const int pos);
	virtual ~CSceneBase(void);

	virtual void Create(XERCES_CPP_NAMESPACE::DOMElement const * node, ENetConfig const &netConfig);
	virtual void Create(TCreatorEntities const &CreatorEntities, TCreatorNodes const &CreatorNodes, TCreatorLights const &CreatorLights, ENetConfig IsClient = e_NCNone, Vector3 &CameraStartPos = Vector3(200.0f, 150.0f, 200.0f));
	virtual void Destroy(void);

	virtual void AddEntities(TCreatorEntities const &CreatorEntities);
	virtual void AddNodes(TCreatorNodes const &CreatorNodes);

	virtual void AddGeometries(TCreatorEntities const &CreatorEntities, TCreatorNodes const &CreatorNodes);
	virtual void RemoveGeometries(void);
	
	virtual void Attach(void);
	virtual void Detach(void);

	virtual void AddLights(TCreatorLights const &CreatorLights);

	virtual void StartAnimations(void);
	virtual void StopAnimations(void);

	virtual SProjectorFrustum CreateProjector(String const &ProjectorName, Vector3 const &Position, Vector3 const &Direction);
	virtual SProjectorFrustum CreateProjector(String const &ProjectorName, Vector3 const &Position, Quaternion const &Orientation);
	virtual void ApplyDecalToMaterial(String const &TextureSourceName, String const &MatName, Frustum * pDecalFrustum, TextureUnitState::TextureAddressingMode const &TextureMode = TextureUnitState::TAM_WRAP);

	virtual void SetAnimationsFrame(Real const &Frame);
	virtual Real GetAnimationsFrame(void);

	virtual bool frameStarted(const FrameEvent& evt);
	virtual bool frameEnded(const FrameEvent& evt);
	
	virtual void addEvent(SOpenMoveDisplayPacket const &packet, Real const startTime);
	virtual void processEvents(void);
	virtual void deleteEvents(void);

	virtual void Reset(void);

	virtual void keyReleased(KeyEvent* e);
	virtual void keyClicked(KeyEvent* e);
	virtual void keyPressed(KeyEvent* e);

	virtual void SetCamera(Vector3 const &position, Quaternion const &orientation, bool fake = false);

	virtual void updateAnim(const FrameEvent &evt);

	Entity * findEntity(String &Name);
	Light * findLight(String &Name);
	CompositorInstance * findCompositor(String &Name);

	void fakeAddEvent(int Id, Real Duration, String &params = String(""), Real const speed = 0.5f, Vector3 const pos = Vector3(0, 0, 0), SOpenMoveDisplayPacket::MessageType type = SOpenMoveDisplayPacket::EVENT);

	void AddListeners(void);
	void RemoveListeners(void);

	virtual void mouseMoved(MouseEvent* e);
	virtual void mouseDragged(MouseEvent* e);
	virtual void mouseDragMoved(MouseEvent* e) {};
	virtual void mouseClicked(MouseEvent* e) {};
	virtual void mouseEntered(MouseEvent* e) {};
	virtual void mouseExited(MouseEvent* e) {};
	virtual void mousePressed(MouseEvent* e);
	virtual void mouseReleased(MouseEvent* e);
	virtual void mouseDragEntered(MouseEvent* e) {};
	virtual void mouseDragExited(MouseEvent* e) {};
	virtual void mouseDragDropped(MouseEvent* e) {};
	virtual void setupEventHandlers(void);
	virtual bool handleQuitEvent(const CEGUI::EventArgs& e);
	
	virtual void setupSheet(void);
	virtual void setGUI(MovableObject *currentObject = NULL);
	virtual void updateGUI(void);

	bool handleThumbTrackStarted(const CEGUI::EventArgs& e);
	bool handleThumbTrackEnded(const CEGUI::EventArgs& e);
	bool handleScrollPositionChanged(const CEGUI::EventArgs& e);

	void notifyMaterialSetup(uint32 pass_id, MaterialPtr &mat);
	void notifyMaterialRender(uint32 pass_id, MaterialPtr &material);

	void setTransitionFromXml(STransition &transition, XERCES_CPP_NAMESPACE::DOMElement const *xml);

	//Variables
public:
	COgreEngine & m_OgreEngine;
	Camera * m_pCurrentCamera;
	TFrameListeners m_FrameListeners;
	TEntities m_Entities;
	TKeyListeners m_KeyListeners;
	TLights m_Lights;
	TFrustums m_Frustums;
	SceneNode * m_pSceneNode;
	String m_SceneName;
	bool m_ShowEntitiesBBox;

	TFrameListeners m_AnimFrameListeners;
	TKeyListeners m_AnimKeyListeners;

	ExampleFrameListener * m_pCameraFrameListener;

	Bone * m_pCameraBone;
	Entity * m_pCameraMoveEntity;
	ENetConfig m_NetConfig;
	TEvents m_Events;
	TEvents m_EventsList;

	CSceneTimer m_Timer;
	Real m_NetTimer;

	TSceneLights m_SceneLights;
	ColourValue m_AmbientColour;

	TCompositors m_Compositors;
	Degree m_CameraRot;

	int m_ScenePos;

	SceneNode * m_pCameraFrutsumNode[e_NbCamera];

	Camera * m_pCameraFrutsum[e_NbCamera];

	CEGUI::Scrollbar * m_TimeSlider;
	bool m_sliding;

	unsigned long m_NextEventId;
	
	SResetParams m_resetParams;

	CEGUI::Window* m_GuiSheet;
	size_t m_CurrentItemSelection;
	Real m_LastSelectionTime;

	static LightEditor * m_LightEditorSheet;

	MovableObject * m_CurrentObject;

	STransition m_transitionOut;
	STransition m_transitionIn;

	bool m_isSceneReady;
	unsigned int nbFrameElapsed;

	CompositorInstance * m_transitionCompositor[2];

	bool m_transitionInIsPassed;
	bool m_canSetScene;

	Real m_interactionTime;

	Real m_animPacket[MAX_ANIM];
};

inline bool operator == (CSceneBase const &scene, int id)
{
	return (scene.m_ScenePos == id);
}

#endif //__SCENEBASE_H__